perm filename TEST.L[FTL,LSP] blob
sn#826373 filedate 1986-10-21 generic text, type T, neo UTF8
;;;-*- Mode:LISP; Package: PCL; Base:10; Syntax:Common-lisp -*-
;;;
;;; *************************************************************************
;;; Copyright (c) 1985 Xerox Corporation. All rights reserved.
;;;
;;; Use and copying of this software and preparation of derivative works
;;; based upon this software are permitted. Any distribution of this
;;; software or derivative works must comply with all applicable United
;;; States export control laws.
;;;
;;; This software is made available AS IS, and Xerox Corporation makes no
;;; warranty about the software, its performance or its conformity to any
;;; specification.
;;;
;;; Any person obtaining a copy of this software is requested to send their
;;; name and post office or electronic mail address to:
;;; CommonLoops Coordinator
;;; Xerox Artifical Intelligence Systems
;;; 2400 Hanover St.
;;; Palo Alto, CA 94303
;;; (or send Arpanet mail to CommonLoops-Coordinator.pa@Xerox.arpa)
;;;
;;; Suggestions, comments and requests for improvements are also welcome.
;;; *************************************************************************
;;;
;;; Testing code.
;;;
;;; Because CommonLoops runs in itself so much, the notion of a test file for
;;; it is kind of weird.
;;;
;;; If all of PCL loads then many of the tests in this file (particularly
;;; those at the beginning) are sure to work. Those tests exists primarily
;;; to help debug things when low-level changes are made to PCL, or when a
;;; particular port customizes low-level code.
;;;
;;; Some of the other tests are "real" in the sense that they test things
;;; that PCL itself does not use, so might be broken.
;;;
;;; NOTE:
;;; The tests in this file do not appear in random order! They
;;; depend on state which has already been set up in order to run.
;;;
;;; As a convention foo, bar and baz are used for classes and
;;; discriminators which are just for the current test. By
;;; default, do-test resets those names before running the current
;;; test. Other names like x, y, z, method-1... are used to name
;;; classes and discriminators which last the life of the file.
;;;
(defvar *without-errors*
(or #+Symbolics #'(lambda (form)
`(multiple-value-bind (.values. .errorp.)
(si::errset ,form nil)
(declare (ignore .values.))
.errorp.))
nil))
(defmacro without-errors (&body body)
(if *without-errors*
(funcall *without-errors* `(progn ,@body))
(error "Calling WITHOUT-ERRORS when *without-errors* is nil.")))
(defmacro do-test (name&options &body body)
(let ((name (if (listp name&options) (car name&options) name&options))
(options (if (listp name&options) (cdr name&options) ())))
(keyword-bind ((clear t)
(should-error nil))
options
(cond ((and should-error (null *without-errors*))
`(format t
"~&Skipping testing ~A,~%~
because can't ignore errors in this Common Lisp."
',name))
(t
`(progn
(format t "~&Testing ")
(format t ,name)
(format t "... ")
,(when clear
'(progn (dolist (x '(foo bar baz))
(setf (discriminator-named x) nil)
(fmakunbound x)
(setf (class-named x) nil))))
(if ,(if should-error
`(without-errors (progn ,@body))
`(progn ,@body))
(format t "OK")
(progn (format t "FAILED")
(error "Test Failed: ~A" ',name)))))))))
(defun permutations (elements length)
(if (= length 1)
(iterate ((x in elements)) (collect (list x)))
(let ((sub-permutations (permutations elements (- length 1))))
(iterate ((x in elements))
(join (iterate ((y in sub-permutations))
(collect (cons x y))))))))
;;
;;;;;;
;;
(eval-when (load eval)
(format t "~&~%~%Testing Extremely low-level stuff..."))
(do-test ("Memory Block Primitives" :clear nil)
(let ((block (make-memory-block 10))
(tests (iterate ((i from 0 below 10)) (collect (make-list 1)))))
(and (numberp (memory-block-size block))
(= (memory-block-size block) 10)
(progn (iterate ((i from 0) (test in tests))
(setf (memory-block-ref block i) test))
(iterate ((i from 0) (test in tests))
(unless (eq (memory-block-ref block i) test) (return nil))
(finally (return t)))))))
(do-test ("Class Wrapper Caching" :clear nil)
(let* ((wrapper (make-class-wrapper 'test))
(offset (class-wrapper-get-slot-offset wrapper 'foo))
(value (list ())))
(and (eq 'foo (setf (class-wrapper-cached-key wrapper offset) 'foo))
(eq value (setf (class-wrapper-cached-val wrapper offset) value))
(eq 'foo (class-wrapper-cached-key wrapper offset))
(eq value (class-wrapper-cached-val wrapper offset)))))
(do-test ("Flushing Class-Wrapper caches" :clear nil)
(let* ((wrapper (make-class-wrapper 'test))
(offset (class-wrapper-get-slot-offset wrapper 'foo)))
(setf (class-wrapper-cached-key wrapper offset) 'foo)
(flush-class-wrapper-cache wrapper)
(neq 'foo (class-wrapper-cached-key wrapper offset))))
(do-test "Class Wrapper Caching"
(let ((slots '(;; Some random important slots.
name class-wrapper class-precedence-list
direct-supers direct-subclasses direct-methods
no-of-instance-slots instance-slots
local-supers
non-instance-slots local-slots prototype))
(wrapper (make-class-wrapper 'test))
(hits 0))
(iterate ((slot in slots))
(let ((offset (class-wrapper-get-slot-offset wrapper slot)))
(setf (class-wrapper-cached-key wrapper offset) slot)))
(iterate ((slot in slots))
(let ((offset (class-wrapper-get-slot-offset wrapper slot)))
(and (eq (class-wrapper-cached-key wrapper offset) slot)
(incf hits))))
(format t
" (~D% hit) "
(* 100.0 (/ hits (float (length slots)))))
t))
(do-test "static slot-storage"
(let ((static-slots (%allocate-static-slot-storage--class 5)))
(iterate ((i from 0))
(when (= i 5) (return t))
(let ((cons (list ()))
(index (%convert-slotd-position-to-slot-index i)))
(setf (%static-slot-storage-get-slot--class static-slots index) cons)
(or (eq cons
(%static-slot-storage-get-slot--class static-slots index))
(return nil))))))
(eval-when (load eval) (format t "~&~%~%Testing High-Level stuff..."))
(defvar *built-in-classes*
'((T T)
(NUMBER 1)
(RATIO 1/2 1/2)
(COMPLEX)
(INTEGER 1)
(RATIO)
(FIXNUM most-positive-fixnum most-positive-fixnum)
(BIGNUM (+ most-positive-fixnum 1) (+ most-positive-fixnum 1))
SHORT-FLOAT SINGLE-FLOAT DOUBLE-FLOAT LONG-FLOAT
(FLOAT 1.1)
(NULL () ())
(STANDARD-CHAR #\a)
(STRING-CHAR #\a)
(CHARACTER #\a #\a)
BIT-VECTOR
(STRING (make-string 1) (make-string 1))
(ARRAY (make-array 1))
SIMPLE-ARRAY SIMPLE-VECTOR SIMPLE-STRING SIMPLE-BIT-VECTOR
(VECTOR (make-string 1))
(VECTOR (make-array 1))
(LIST '(1 2 3))
(SEQUENCE (make-string 1))
(SEQUENCE (make-array 1))
(SEQUENCE (make-list 1))
(HASH-TABLE (make-hash-table :size 1) (make-hash-table :size 1))
(READTABLE *readtable* *readtable*)
(PACKAGE *package* *package*)
(PATHNAME (make-pathname :name "foo") (make-pathname :name "foo"))
(STREAM *terminal-io* *terminal-io*)
(RANDOM-STATE (make-random-state) (make-random-state))
(CONS (cons 1 2) (cons 1 2))
(SYMBOL 'foo 'foo)
COMMON))
(do-test "existence of built-in classes"
(not (dolist (entry *built-in-classes*)
(let ((type (if (listp entry) (car entry) entry)))
(or (class-named type t)
(progn (format t "Missing the built-in class named: ~S" type)
(return t)))))))
;;; See how CLASS-OF works.
(eval-when (load eval)
(format t "~%Check to see how well portable CLASS-OF works... ")
(let ((lost ()))
(dolist (entry *built-in-classes*)
(or (not (listp entry))
(null (cddr entry))
(let* ((thing (eval (caddr entry)))
(class (class-of thing)))
(and class (eq (class-name class) (car entry))))
(progn (setq lost t)
(format t
"~&WARNING: Can't define methods on: ~S."
(car entry)))))
(when lost (terpri) (terpri))
(format t "OK")))
(do-test "existence of discriminators for accessors of early classes"
;; Because accessors are done with add-method, and this has to be done
;; specially for early classes it is worth testing to make sure that
;; the discriminators got created for the accessor of early classes.
(not
(dolist (class '(t object essential-class class discriminator method))
(setq class (class-named class))
(or (not (dolist (slotd (class-instance-slots class))
(and (slotd-accessor slotd)
(or (discriminator-named (slotd-accessor slotd))
(return nil)))))
(not (dolist (slotd (class-non-instance-slots class))
(and (slotd-accessor slotd)
(or (discriminator-named (slotd-accessor slotd))
(return nil)))))))))
(do-test "a simple defstruct"
(ndefstruct (x (:class class))
(a 1)
(b 2))
(and (fboundp 'make-x)
(fboundp 'x-p)
(fboundp 'copy-x)
(fboundp 'x-a)
(fboundp 'x-b)
(typep--class (make-x) 'x)
(x-p (make-x))
(equal (x-a (make-x)) 1)
(equal (x-a (make-x :a 3)) 3)
(x-p (copy-x (make-x)))
))
(do-test "obsolete-class stuff"
(and (class-named 'obsolete-class)
(let ((old-x-class (class-named 'x))
(old-x-instance (make-x)))
(ndefstruct (x (:class class))
(a 3))
(and (neq (class-of old-x-instance) (class-named 'x))
(= (x-a old-x-instance) 1)))))
(do-test "multiple constructors"
(ndefstruct (x (:class class)
(:constructor make-x)
(:constructor make-x-1 (a b)))
a
b)
(and (fboundp 'make-x)
(fboundp 'make-x-1)
(equal (get-slot (make-x :a 1 :b 2) 'a) 1)
(equal (get-slot (make-x :a 1 :b 2) 'b) 2)
(equal (get-slot (make-x-1 2 1) 'a) 2)
(equal (get-slot (make-x-1 2 1) 'b) 1)))
(do-test "the :print-function defstruct-option"
(ndefstruct (x (:class class)
(:print-function x-print-function))
a
b)
(defun x-print-function (object stream level)
(when (and (x-p object)
(streamp stream) ;Don't be breaking my test file
(numberp level)) ;because of your problems.
(throw 'x 'x)))
(eq (catch 'x (prin1 (make 'x))) 'x))
;;; ** need more tests in here,
;;; test the basic iwmc-class structure
;;; test class-wrappers some more
;;;
;;; OK, now we know that simple defstruct works and that obsolete classes work.
;;; Now we set up some real simple classes that we can use for the rest of the
;;; file.
;;;
(ndefstruct (i (:class class))) ;(i ..)
(ndefstruct (j (:class class))) ;(j ..)
(ndefstruct (k (:class class))) ;(k ..)
(ndefstruct (l (:class class) (:include (i)))) ;(l i ..)
(ndefstruct (m (:class class) (:include (i j)))) ;(m i j ..)
(ndefstruct (n (:class class) (:include (k)))) ;(n k ..)
(ndefstruct (q (:class class) (:include (i)))) ;(q i ..)
(ndefstruct (r (:class class) (:include (m)))) ;(r m i j ..)
(ndefstruct (s (:class class) (:include (n i k)))) ;(s n i k ..)
(do-test "classical methods"
(defmeth foo ((x i)) x 'i)
(defmeth foo ((x n)) x 'n)
(defmeth foo ((x s)) x 's)
(and (eq (foo (make-i)) 'i)
(eq (foo (make-n)) 'n)
(eq (foo (make-s)) 's)))
(do-test "run-super"
(defmeth foo (o) o ())
(defmeth foo ((o i)) o (cons 'i (run-super)))
(defmeth foo ((o m)) o (cons 'm (run-super)))
(defmeth foo ((o n)) o (cons 'n (run-super)))
(defmeth foo ((o q)) o (cons 'q (run-super)))
(defmeth foo ((o r)) o (cons 'r (run-super)))
(defmeth foo ((o s)) o (cons 's (run-super)))
(let ((i (make-i)) (m (make-m)) (q (make-q)) (r (make-r)) (s (make-s)))
(and (equal (foo i) '(i))
(equal (foo m) '(m i))
(equal (foo q) '(q i))
(equal (foo r) '(r m i))
(equal (foo s) '(s n i)))))
(do-test "multi-methods when first 3 args are discriminated on"
(let ((permutations (permutations '(i n r) 3)))
(mapcar #'(lambda (p)
(EVAL `(defmeth foo ,(mapcar 'list '(x y z) p) x y z ',p)))
permutations)
(every #'(lambda (p)
(equal (apply 'foo (mapcar 'make p)) p))
permutations)))
(do-test "multi-methods when assorted args are discriminated on"
(let ((permutations (permutations '(i n r nil) 3)))
(mapc #'(lambda (p)
(EVAL `(defmeth foo
,(mapcar #'(lambda (arg type-spec)
(if type-spec
(list arg type-spec) arg))
'(arg1 arg2 arg3)
p)
arg1 arg2 arg3 ',p)))
permutations)
(every #'(lambda (p)
(equal (apply 'foo
(mapcar #'(lambda (x) (and x (make x))) p)) p))
permutations)))
;(do-test "anonymous discriminators"
;
; (let ((foo (make 'discriminator))
; (proto-method (class-prototype (class-named 'method))))
; (add-method-internal foo proto-method '(thing) (list (class-named 'x)) '(lambda (thing) thing 'x))
; (add-method foo '(thing) (list (class-named 'y)) '(lambda (thing) thing 'y))
; (add-method foo '(thing) (list (class-named 'z)) '(lambda (thing) thing 'z))
;
; (let ((function (discriminator-discriminating-function foo)))
; (and (eq (funcall function (make 'x)) 'x)
; (eq (funcall function (make 'y)) 'y)
; (eq (funcall function (make 'z)) 'z)))))
(do-test "Simple with test -- does not really exercise the walker."
(ndefstruct (foo (:class class))
(x 0)
(y 0))
(defmeth foo ((obj foo))
(with (obj)
(list x y)))
(defmeth bar ((obj foo))
(with ((obj obj-))
(setq obj-x 1
obj-y 2)))
(and (equal '(0 0) (foo (make-foo)))
(equal '(1 2) (foo (make-foo :x 1 :y 2)))
(let ((foo (make-foo)))
(bar foo)
(and (equal (get-slot foo 'x) 1)
(equal (get-slot foo 'y) 2)))))
(do-test "Simple with* test -- does not really exercise the walker."
(ndefstruct (foo (:class class))
(x 0)
(y 0))
(defmeth foo ((obj foo))
(with* (obj)
(list x y)))
(defmeth bar ((obj foo))
(with* ((obj obj-))
(setq obj-x 1
obj-y 2)))
(and (equal '(0 0) (foo (make-foo)))
(equal '(1 2) (foo (make-foo :x 1 :y 2)))
(let ((foo (make-foo)))
(bar foo)
(and (equal (get-slot foo 'x) 1)
(equal (get-slot foo 'y) 2)))))
'(
;;; setup for :daemon combination test
;;;
(do-test "setting up for :daemon method combination test"
(ndefstruct (foo (:class class)))
(ndefstruct (bar (:class class) (:include (foo))))
(ndefstruct (baz (:class class) (:include (bar)))))
(defvar *foo*)
(defmeth foo ((x foo)) (push 'foo *foo*) 'foo)
(defmeth (foo :before) ((x foo)) (push '(:before foo) *foo*))
(defmeth (foo :after) ((x foo)) (push '(:after foo) *foo*))
(do-test (":before primary and :after all on same class." :clear nil)
(let ((*foo* ()))
(and (eq (foo (make 'foo)) 'foo)
(equal *foo* '((:after foo) foo (:before foo))))))
(defmeth foo ((x bar)) (push 'bar *foo*) 'bar)
(do-test (":before and :after inherited, primary from this class" :clear nil)
(let ((*foo* ()))
(and (eq (foo (make 'bar)) 'bar)
(equal *foo* '((:after foo) bar (:before foo))))))
(do-test ("make sure shadowing primary in sub-class has no effect here"
:clear nil)
(let ((*foo* ()))
(and (eq (foo (make 'foo)) 'foo)
(equal *foo* '((:after foo) foo (:before foo))))))
(defmeth (foo :before) ((x bar)) (push '(:before bar) *foo*))
(defmeth (foo :after) ((x bar)) (push '(:after bar) *foo*))
(do-test (":before both here and inherited~%~
:after both here and inherited~%~
primary from here"
:clear nil)
(let ((*foo* ()))
(and (eq (foo (make 'bar)) 'bar)
(equal (reverse *foo*)
'((:before bar) (:before foo) bar (:after foo) (:after bar))))))
(defmeth foo ((x baz)) (push 'baz *foo*) 'baz)
(do-test ("2 :before and 2 :after inherited, primary from here" :clear nil)
(let ((*foo* ()))
(and (eq (foo (make 'baz)) 'baz)
(equal (reverse *foo*)
'((:before bar) (:before foo) baz (:after foo) (:after bar))))))
(do-test "setting up for :list method combination test"
(make-specializable 'foo :arglist '(x) :method-combination-type :list)
(ndefstruct (foo (:class class)))
(ndefstruct (bar (:class class) (:include (foo))))
(ndefstruct (baz (:class class) (:include (bar)))))
(defmeth foo ((x foo)) 'foo)
(do-test ("single method, :list combined, from here" :clear nil)
(equal (foo (make 'foo)) '(foo)))
(defmeth foo ((x bar)) 'bar)
(do-test ("method from here and one inherited, :list combined" :clear nil)
(equal (foo (make 'bar)) '(foo bar)))
(defmeth foo ((x baz)) 'baz)
(do-test ("method from here, two inherited, :list combined" :clear nil)
(equal (foo (make 'baz)) '(foo bar baz)))
(do-test ("make sure that more specific methods aren't in my combined method"
:clear nil)
(and (equal (foo (make 'foo)) '(foo))
(equal (foo (make 'bar)) '(foo bar))
(equal (foo (make 'baz)) '(foo bar baz))))
)
;;
;;;;;; things that bug fixes prompted.
;;
(do-test "with inside of lexical closures"
;; 6/20/86
;; The walker was confused about what (FUNCTION (LAMBDA ..)) meant. It
;; didn't walk inside there. Its sort of surprising this didn't get
;; caught sooner.
(ndefstruct (foo (:class class))
(x 0)
(y 0))
(defun foo (fn foos)
(and foos (cons (funcall fn (car foos)) (foo fn (cdr foos)))))
(defun bar ()
(let ((the-foo (make 'foo :x 0 :y 3)))
(with ((the-foo () foo))
(foo #'(lambda (foo) (incf x) (decf y))
(make-list 3)))))
(equal (bar) '(2 1 0)))
(do-test "redefinition of default method has proper effect"
;; 5/26/86
;; This was caused because the hair for trying to avoid making a
;; new discriminating function didn't know that changing the default
;; method was a reason to make a new discriminating function. Fixed
;; by always making a new discriminating function when a method is
;; added or removed. The template stuff should keep this from being
;; expensive.
(defmeth foo ((x class)) 'class)
(defmeth foo (x) 'default)
(defmeth foo (x) 'new-default)
(eq (foo nil) 'new-default))
(do-test ("extra keywords in init-plist cause an error" :should-error t)
;; 5/26/86
;; Remember that Common-Lisp defstruct signals errors if there are
;; extra keywords in the &rest argument to make-foo.
(ndefstruct (foo (:class class)) a b c)
(make 'foo :d 3))
(do-test "run-super with T specifier for first arg"
;; 5/29/86
;; This was caused because run-super-internal didn't know about the
;; type-specifier T being special. This is yet another reason to
;; flush that nonsense about keeping T special.
(defmeth foo (x y) '((t t)))
(defmeth foo (x (y k)) '((t k)))
(defmeth foo (x (y n)) (cons '(t n) (run-super)))
(defmeth foo ((x i) (y k)) '((i k)))
(defmeth foo ((x l) (y n)) (cons '(l n)